java高并发秒杀活动的各种简单实现【springBoot+mybatis+redis+mysql】 |
您所在的位置:网站首页 › java 并发处理xml › java高并发秒杀活动的各种简单实现【springBoot+mybatis+redis+mysql】 |
最近遇到比较多数据不一致的问题,大多数都是因为并发请求时,没及时处理的原因,故用一个比较有代表性的业务场景【活动秒杀】来模拟一下这个这种高并发所产生的问题。 首先搭建一个springboot项目在这里我做演示了,不会的可以自行百度,搭建过程很简单。 1:搭建好的项目目录结构
2:商品表(记录商品名称,本次可以秒杀的库存量)
加了一条记录(后面每次测试都先手动把库存恢复成100才进行测试)
3:实体类(这里不用实体类也可以,根据自己的需求来)
一、不做任何处理的高并发秒杀实现(错误演示): 1.Controller层,模拟500个并发调用: package com.mybatis.controller; import com.mybatis.domain.BaseResponse;import com.mybatis.domain.MiaoshaRequest;import com.mybatis.service.MiaoshaService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*; @Controller@RequestMapping(value="/miaoshagoods")public class MiaoshaController { @Autowired public MiaoshaService miaoshaService; @PostMapping("/miaosha_java_sql_lock") public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){ BaseResponse response=new BaseResponse(); for(int i=0;i0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } return response; }}3.dao层(mybatis的xml文件): select from miao_sha_goods where goods_name = #{goodsName,jdbcType=VARCHAR} update miao_sha_goods set goods_sum = #{goodsSum,jdbcType=INTEGER} where goods_name = #{goodsName,jdbcType=VARCHAR} 4.测试结果:
截图表明,居然有500个人抢购成功,而且库存量却只减少了12个,这是明显是错误的。 二、数据库乐观锁处理的高并发秒杀实现:
package com.mybatis.controller; import com.mybatis.domain.BaseResponse;import com.mybatis.domain.MiaoshaRequest;import com.mybatis.service.MiaoshaService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*; @Controller@RequestMapping(value="/miaoshagoods")public class MiaoshaController { @Autowired public MiaoshaService miaoshaService; @PostMapping("/miaosha_java_sql_lock") public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){ BaseResponse response=new BaseResponse(); for(int i=0;i0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } return response; } /** * 数据库乐观锁实现秒杀 * @param request * @return */ @Override public BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) { int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); //做出相应的逻辑(记录抢购成功的用户名什么的....) }else{ System.out.println("抢到iphoneX,失败!"); //重试或者返回友好的提示什么的.... } return response; } } 3.dao层(mybatis的xml文件)[在SQL层面改为数据库乐观锁]: select from miao_sha_goods where goods_name = #{goodsName,jdbcType=VARCHAR} update miao_sha_goods set goods_sum = #{goodsSum,jdbcType=INTEGER},version=version+1 where goods_name = #{goodsName,jdbcType=VARCHAR} and version = #{version,jdbcType=VARCHAR} 4.测试结果:
截图表明,总共有500个人抢,有29个人抢购成功,而且库存量减少了29个,这保证了库存的正确性。但却会有抢购不成功的请求,需要我们后续去处理。 三、数据库悲观锁处理的高并发秒杀实现: 1.Controller层,模拟500个并发调用: package com.mybatis.controller; import com.mybatis.domain.BaseResponse; import com.mybatis.domain.MiaoshaRequest; import com.mybatis.service.MiaoshaService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping(value="/miaoshagoods") public class MiaoshaController { @Autowired public MiaoshaService miaoshaService; @PostMapping("/miaosha_java_sql_lock") public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){ BaseResponse response=new BaseResponse(); for(int i=0;i0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } return response; } /** * 数据库乐观锁实现秒杀 * @param request * @return */ @Override public BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) { int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); //做出相应的逻辑(记录抢购成功的用户名什么的....) }else{ System.out.println("抢到iphoneX,失败!"); //重试或者返回友好的提示什么的.... } return response; } /** * 数据库悲观锁实现秒杀 * @param request * @return */ @Override @Transactional public BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) { int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } return response; } } 3.dao层(mybatis的xml文件)[在SQL层面改为数据库悲观锁]: select from miao_sha_goods where goods_name = #{goodsName,jdbcType=VARCHAR} FOR UPDATE update miao_sha_goods set goods_sum = #{goodsSum,jdbcType=INTEGER} where goods_name = #{goodsName,jdbcType=VARCHAR} 4.测试结果:
截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性。
四、java线程同步锁处理的高并发秒杀实现: 1.Controller层,模拟500个并发调用: package com.mybatis.controller; import com.mybatis.domain.BaseResponse; import com.mybatis.domain.MiaoshaRequest; import com.mybatis.service.MiaoshaService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping(value="/miaoshagoods") public class MiaoshaController { @Autowired public MiaoshaService miaoshaService; @PostMapping("/miaosha_java_sql_lock") public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){ BaseResponse response=new BaseResponse(); for(int i=0;i0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } return response; } /** * 数据库乐观锁实现秒杀 * @param request * @return */ @Override public BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) { int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); //做出相应的逻辑(记录抢购成功的用户名什么的....) }else{ System.out.println("抢到iphoneX,失败!"); //重试或者返回友好的提示什么的.... } return response; } /** * 数据库悲观锁实现秒杀 * @param request * @return */ @Override @Transactional public BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) { int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } return response; } /** * java同步锁实现秒杀 * @param request * @return */ @Override public BaseResponse miaoshaGoods_java_synchronized_lock(MiaoshaRequest request,BaseResponse response) { synchronized(this){ int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } } return response; } } 3.dao层(mybatis的xml文件): select from miao_sha_goods where goods_name = #{goodsName,jdbcType=VARCHAR} update miao_sha_goods set goods_sum = #{goodsSum,jdbcType=INTEGER} where goods_name = #{goodsName,jdbcType=VARCHAR} 4.测试结果:
截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性。 五、java线程可重入锁处理的高并发秒杀实现: 1.Controller层,模拟500个并发调用: package com.mybatis.controller; import com.mybatis.domain.BaseResponse; import com.mybatis.domain.MiaoshaRequest; import com.mybatis.service.MiaoshaService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping(value="/miaoshagoods") public class MiaoshaController { @Autowired public MiaoshaService miaoshaService; @PostMapping("/miaosha_java_sql_lock") public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){ BaseResponse response=new BaseResponse(); for(int i=0;i0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } return response; } /** * 数据库乐观锁实现秒杀 * @param request * @return */ @Override public BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) { int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); //做出相应的逻辑(记录抢购成功的用户名什么的....) }else{ System.out.println("抢到iphoneX,失败!"); //重试或者返回友好的提示什么的.... } return response; } /** * 数据库悲观锁实现秒杀 * @param request * @return */ @Override @Transactional public BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) { int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } return response; } /** * java同步锁实现秒杀 * @param request * @return */ @Override public BaseResponse miaoshaGoods_java_synchronized_lock(MiaoshaRequest request,BaseResponse response) { synchronized(this){ int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } } return response; } /** * java可重入锁实现秒杀 * @param request * @return */ @Override public BaseResponse miaoshaGoods_java_reentrant_lock(MiaoshaRequest request,BaseResponse response) { lock.lock(); int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods); } if(countSuc==1){ System.out.println(request.getGoodNames()+"抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } lock.unlock(); return response; } } 3.dao层(mybatis的xml文件): select from miao_sha_goods where goods_name = #{goodsName,jdbcType=VARCHAR} update miao_sha_goods set goods_sum = #{goodsSum,jdbcType=INTEGER} where goods_name = #{goodsName,jdbcType=VARCHAR} 4.测试结果:
截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性。 六、redis单线程处理的高并发秒杀实现(推荐): 1.Controller层,模拟500个并发调用: package com.mybatis.controller; import com.mybatis.domain.BaseResponse; import com.mybatis.domain.MiaoshaRequest; import com.mybatis.service.MiaoshaService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping(value="/miaoshagoods") public class MiaoshaController { @Autowired public MiaoshaService miaoshaService; @Autowired private RedisTemplate redisTemplate; @PostMapping("/miaosha_java_sql_lock") public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){ BaseResponse response=new BaseResponse(); for(int i=0;i0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); //做出相应的逻辑(记录抢购成功的用户名什么的....) }else{ System.out.println("抢到iphoneX,失败!"); //重试或者返回友好的提示什么的.... } return response; } /** * 数据库悲观锁实现秒杀 * @param request * @return */ @Override @Transactional public BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) { int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } return response; } /** * java同步锁实现秒杀 * @param request * @return */ @Override public BaseResponse miaoshaGoods_java_synchronized_lock(MiaoshaRequest request,BaseResponse response) { synchronized(this){ int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods); } if(countSuc==1){ System.out.println("抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } } return response; } /** * java可重入锁实现秒杀 * @param request * @return */ @Override public BaseResponse miaoshaGoods_java_reentrant_lock(MiaoshaRequest request,BaseResponse response) { lock.lock(); int countSuc=0; MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames()); if(miaoShaGoods.getGoodsSum()>0){ miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1); countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods); } if(countSuc==1){ System.out.println(request.getGoodNames()+"抢到iphoneX,成功!"); }else{ System.out.println("抢到iphoneX,失败!"); } lock.unlock(); return response; } @Override public Integer getGoodsSum(MiaoshaRequest request) { MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames()); return miaoShaGoods.getGoodsSum(); } /** * redis实现秒杀 * @param response * @return */ @Override public BaseResponse miaoshaGoods_redis(MiaoshaRequest request,BaseResponse response) { //增量计算剩余库存(利用redis的单线程特性) double goodsSurplusSum=redisTemplate.opsForValue().increment(request.getGoodNames()+":goodsSum",-1); if(goodsSurplusSum>=0){ System.out.println("抢到iphoneX,成功!还剩下:"+goodsSurplusSum); }else { System.out.println("抢到iphoneX,失败!"); } return response; } }3.测试结果:
截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性,抢后redis显示是-400个,说明有500个人进来了,有400个人抢不到。综上所述,要控制库存量,一般要用锁机制。但是一般加锁的话会比较影响性能(只能用于单服务),推荐大家使用redis自带的线程安全属性来实现(可实现分布式锁)。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |